package at.fhooe.stefanirndorfer.quadtree;

import at.fhooe.stefanirndorfer.entity.EntityManager;
import at.fhooe.stefanirndorfer.game.Game;
import at.fhooe.stefanirndorfer.logging.Log;

import static at.fhooe.stefanirndorfer.utilities.Utilities.E;
import static at.fhooe.stefanirndorfer.utilities.Utilities.N;
import static at.fhooe.stefanirndorfer.utilities.Utilities.W;
import static at.fhooe.stefanirndorfer.utilities.Utilities.S;
import static at.fhooe.stefanirndorfer.utilities.Utilities.NW;
import static at.fhooe.stefanirndorfer.utilities.Utilities.NE;
import static at.fhooe.stefanirndorfer.utilities.Utilities.SW;
import static at.fhooe.stefanirndorfer.utilities.Utilities.SE;

public class QuadTree {

	int minSize;
	int maxSize;
	int maxWallPoints;
	QuadTreeNode rootNode;
	
	private EntityManager em;
	
	//Debug
	int leafCounter;
	
	public QuadTree(int minSize, int maxSize, int maxWallPoints, double[] center){
		this.minSize = minSize;
		this.maxSize = maxSize;
		this.maxWallPoints = maxWallPoints;
		
		rootNode = new QuadTreeNode(maxSize, center, null, (short)-1);
		em = Game.getInstance().getEntityManager();
		leafCounter = 0;
		
		//perform tree generation
		generateTree(rootNode);
		Log.info(this, "Leaf-Counter: " + leafCounter);
	}

	
	/**
	 * this method uses the entity-manager to perform collision detections
	 * 
	 * @param center
	 */
	public void generateTree(QuadTreeNode node){
		
		double[] lowerLeft = new double[]{node.getCenter()[0] - node.getSize()/2, node.getCenter()[1] + node.getSize()/2};
		double[] upperRight = new double[]{node.getCenter()[0] + node.getSize()/2, node.getCenter()[1] - node.getSize()/2};
		
		//check recursion-anchor
		switch(isLeafNode(lowerLeft, upperRight)){
		case 0:
			double size = node.getSize()/2;
			
			// sub-divide clockwise (NW,NE,SE,SW) and call generateTree
			
			//childnode SW
			QuadTreeNode childNW = new QuadTreeNode(size, new double[]{lowerLeft[0] + size/2, lowerLeft[1] - size/2}, node, SW);
			node.getChildNodes()[SW] = childNW;
			generateTree(childNW);
			
			//childnode NE
			QuadTreeNode childNE = new QuadTreeNode(size, new double[]{lowerLeft[0] + size * 1.5, lowerLeft[1] - size/2}, node, SE);
			node.getChildNodes()[SE] = childNE;
			generateTree(childNE);
			
			//childnode SE
			QuadTreeNode childSE = new QuadTreeNode(size, new double[]{lowerLeft[0] + size/2, lowerLeft[1] - size * 1.5}, node, NE);
			node.getChildNodes()[NE] = childSE;
			generateTree(childSE);
			
			//childnode SW
			QuadTreeNode childSW = new QuadTreeNode(size, new double[]{lowerLeft[0] + size * 1.5, lowerLeft[1] - size * 1.5}, node, NW);
			node.getChildNodes()[NW] = childSW;
			generateTree(childSW);
			
			break;
			
		case 1:
			node.setIsLeaf();
			break;
			
		case 2:
			//node.setIsLeaf();
			node.setIsWall(true);
			break;
		}
		
		
		
	}
	
	
	/**
	 * returns 1 if node is a room leaf,
	 * returns 2 if node is a wall leaf,
	 * returns 0 if node must be divided
	 * 
	 * @param upperLeft
	 * @param lowerRight
	 * @return
	 */
	private short isLeafNode(double[] upperLeft, double[] lowerRight) {
		
		int size = (int)Math.abs(lowerRight[0] - upperLeft[0]);
		boolean divisable = size/2 >= minSize;
		
		int wallPointCounter = 0;
		int nonWallPointCounter = 0;
		
		int uStartCoord = (int)upperLeft[0];
		int vStartCoord = (int)lowerRight[1];
		
		for (int u = uStartCoord; u < size + uStartCoord; u++) {
			for (int v = vStartCoord; v < size + vStartCoord; v++) {
				
				if(em.checkWallCollisions(u,v)){
					wallPointCounter++;					
				}else{
					nonWallPointCounter++;
				}
				
				if(wallPointCounter > maxWallPoints && !divisable){
					return 2; //a wall leaf
				}
				if(nonWallPointCounter > maxWallPoints && !divisable){
					return 1; // a room leaf
				}
				if((wallPointCounter > maxWallPoints && nonWallPointCounter > maxWallPoints) && divisable){
					return 0; // can be divided, mixed wall or room
				}
			
			}
		}
		
		// a leaf of bigger size than minSize, which is either a wall leaf or a room leaf
		if(wallPointCounter >= nonWallPointCounter){
			return 2; // a wall leaf
		}else{
			leafCounter++;
			return 1; // a room leaf
		}
	}


	//////////////////////////////////////////////////
	// Getters and Setters
	//////////////////////////////////////////////////
	
	
	
	public int getMaxSize() {
		return maxSize;
	}

	public int getMinSize() {
		return minSize;
	}


	public void setMinSize(int minSize) {
		this.minSize = minSize;
	}


	public void setMaxSize(int maxSize) {
		this.maxSize = maxSize;
	}

	public int getAnchorInPixel() {
		return maxWallPoints;
	}

	public void setAnchorInPixel(int anchorInPixel) {
		this.maxWallPoints = anchorInPixel;
	}


	public QuadTreeNode getRootNode() {
		return rootNode;
	}
	
	
	
}

